#include <string>
#include <iostream>
#include <assert.h>
#include "stringutils.h"

using namespace std;


/***********************************
**********  Utils  ***************/
bool StringUtils::noCero(const string& numero){
	bool ret = false;
	int largo = numero.size();
	for(int x = 0; x < largo; x++){
		ret = ret || (numero[x] != '0');
	}
	return ret;
}
void agregarCerosIzq(string& num){
	int cant = (-(num.size()%8))%8;
	for(; cant > 0; cant--)
		num = "0"+num;
}
void agregarCerosDer(string& num){
	int cant = (-(num.size()%8))%8;
	for(; cant > 0; cant--)
		num = num+"0";
}

bool multPorDos(byte* arr, int inicio) {
	int carry = 0;
	while(inicio >= 0) {
		arr[inicio] += arr[inicio];
		arr[inicio] += carry;
		// multiplicar un numero por 2 y sumarle uno tiene a los sumo carry == 1
		if(arr[inicio] > 9) {
			carry = 1;
			arr[inicio] -= 10;
		} else {
			carry = 0;
		}
		inicio--;

	}
	return carry==0;
}


// recibe la parte fraccionaria y la deja lista para ser parseada.
void StringUtils::fracChar2bin(string& numero, int limit) {
	int tamanio = numero.size();
	byte valor;
	byte numArr[tamanio];
	// string => array(char) talque array[0] es la parte mas signficativa
	for(int i=0; i<tamanio; i++) {
		valor = (byte)numero[i]-'0';
		if(!(0 <= valor && valor <=9)){
			cout << ""+numero[i] << endl;
			assert(false);
		}
		numArr[i] = valor;
	}
	// numarr[0] es el mas significativo
	// hacemos la fraccion, tenemos en cuenta los numeros
	// periodicos acotandolos por COTAPORPERIODICO
	tamanio--;
	numero = "";
	int veces = 0;
	while( tamanio >= 0  && (veces < limit*8)) {
		while(tamanio>=0 && numArr[tamanio] == 0){
			tamanio--;}
        numero += (multPorDos(numArr, tamanio)) ? "0" : "1";
		veces ++;
	}
	agregarCerosDer(numero);
}

//reverso de string in place.
void reverse(string &numB2) {
	int tam=numB2.size();
	for(int i=0;i<tam/2;i++) {
		char tmp = numB2[i];
		numB2[i] = numB2[tam-1-i];
		numB2[tam-1-i] = tmp;
	}
}

// recibe un arreglo de char representando numeros, tal que array[0]
// es la parte menos significativa
void dividirPorDos(byte* arr, int inicio, int tamanio) {
	while(inicio < tamanio) {
		byte carry = (arr[inicio]%2) * 10;
		arr[inicio] >>= 1;
		inicio++;
		if(inicio < tamanio)
			arr[inicio] += carry;
	}
}

void StringUtils::entChar2bin(string& numero){
	int tamanio = numero.size();
	byte valor;

	// tranformamos de string de numeros decimales en char a
	// array de numeros decimales en valor(array[0] es
	// la posicion menos signficativa)
	byte numArr[tamanio];
	for(int i=0; i<tamanio; i++) {
		valor = (byte)numero[i]-'0';
		if(!(0 <= valor && valor <=9)){
			cout << ""+numero[i] << endl;
			assert(false);
		}
		numArr[i] = valor;
	}
	//dejamos el numero en el string de forma binaria
	// tal que string[0] es el menos significativo
	int newTam = 0;
	string numB2 = "";
	while(newTam < tamanio || numArr[tamanio - 1] != 0) {
		if(numArr[tamanio - 1]%2 == 0) {
			numB2 +=  "0";
		} else {
			numB2 += "1";
		}
		dividirPorDos(numArr, newTam, tamanio);
		if(numArr[newTam] == 0)
			newTam++;
	}
	numero = numB2;
	// despues del reverse el string[0] es el mas significativo
	reverse(numero);
	agregarCerosIzq(numero);
}

/** recibe entero y fraccion en formato binario requiere que alguno de los dos
 string != a 0*/
int StringUtils::calcularPotencia(const string &ent,string &frac){
	int exponente = 0;
	if(ent.size() > 0 && StringUtils::noCero(ent) ){
		int rest = (ent.size()%8!=0)?1:0;
		exponente = ent.size()/8 + rest;
	} else if (frac.size() > 0){
            while(frac[exponente] == '0'){
                exponente++;
            }
            exponente /= 8;
            if(exponente > 0){
                frac.erase(0,(exponente)*8);
            }
            exponente = -exponente;
	}
    return exponente;
}


bool StringUtils::signoYSeparar(const string& arg_num, string &entero, string &fraccion) {
    string num = arg_num;
    bool signo = num.find("-") != string::npos;
    size_t coma = num.find(",");
    if(coma == string::npos){
        coma = num.find(".");
    }
    entero = num.substr(0, coma);
    if(coma != string::npos){
        fraccion = num.substr(coma + 1, string::npos);
    }
    StringUtils::limpiarEntero(entero);
    StringUtils::limpiarFraccion(fraccion);
    return signo;
}

/** vienene asi: -000000NNNNN */
void StringUtils::limpiarEntero(string& entero) {
    int x = 0;
    while(x < entero.size() && !(entero[x] > '0' && entero[x] <= '9')) {
        x++;
    }
    entero.erase(0, x);
}

void StringUtils::limpiarFraccion(string& fraccion) {
    int x = fraccion.size() - 1;
    while(x >= 0 && !(fraccion[x] > '0' && fraccion[x] <= '9')) {
        x--;
    }
    x++;
    fraccion.erase(x, fraccion.size());
}